package com.leetcode.array;

/**
 * 递增的三元子序列
 * 给你一个整数数组nums ，判断这个数组中是否存在长度为 3 的递增子序列。
 * 如果存在这样的三元组下标 (i, j, k)且满足 i < j < k ，使得nums[i] < nums[j] < nums[k] ，返回 true ；否则，返回 false 。
 * 示例 1：
 * 输入：nums = [1,2,3,4,5]
 * 输出：true
 * 解释：任何 i < j < k 的三元组都满足题意
 * 示例 2：
 * 输入：nums = [5,4,3,2,1]
 * 输出：false
 * 解释：不存在满足题意的三元组
 * 示例 3：
 * 输入：nums = [2,1,5,0,4,6]
 * 输出：true
 * 解释：三元组 (3, 4, 5) 满足题意，因为 nums[3] == 0 < nums[4] == 4 < nums[5] == 6
 * 提示：
 * 1 <= nums.length <= 105
 * -231 <= nums[i] <= 231 - 1
 * 进阶：你能实现时间复杂度为 O(n) ，空间复杂度为 O(1) 的解决方案吗？
 * 作者：力扣 (LeetCode)
 * 链接：https://leetcode-cn.com/leetbook/read/top-interview-questions/xmb141/
 * 来源：力扣（LeetCode）
 * 著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。
 */
class Code334 {
    public static void main(String[] args) {
        int[] nums = {2,1,2,0,4,1};
        boolean flag = increasingTriplet2(nums);
        System.out.println(flag);
    }
    public static boolean increasingTriplet1(int[] nums) {
        int min = Integer.MAX_VALUE;
        int mid = Integer.MAX_VALUE;
        for(int max : nums){
            // 当前遍历的数，为当前遍历到的数中最小的，赋值给min
            if(max < min) {
                min = max;
            }
            // max>min但是max<mid，也就是意味着mid迎来了更好的选择
            else if (max > min && max < mid) {
                mid = max;
            }
            // mid在设定中总是大于min的，所以可以只判断max>mid
            else if (max > mid) {
                return true;
            }
        }
        return false;
    }

    /**
     * 核心想法：遍历一遍数组，希望遍历到的这个数three，前面已经有一个比他小的数two，再前面有一个比two小的数one。
     * 我们需要维护两个变量：one和two。代表递增子序列的第一个数和第二个数。
     * 假设我们已经有了这两个数，那么three的大小有以下三种情况：
     * 1)three大于two
     *   2         5       8
     *  one ..... two    three
     * 此情况下：即找到了三元组，直接返回true。
     *
     * 2)three介于two和one之间（three<=two && three>one）
     *   2         5       4
     *  one ..... two    three
     *
     *  此情况下：应更新two，赋值为这个更小的值。
     *  这相当于帮我们扩大了three的可选择范围，当再次遇到一个比更新过的two大的数即可找到。
     *   2         4       5
     *  one ..... two    three
     *
     * 3)three小于one
     *   2         5       1
     *  one ..... two    three
     *
     *  此情况下：应更新one，赋值为这个更小的值。而不需要动two。
     *  这相当于帮我们扩大了之后出现的two的可选择范围。进而扩大了之后出现的three的可选择范围
     *
     *   1         5       2
     *  one ..... two    three
     *   1         2       5
     *  one ..... two    three
     *
     * 需要注意的是，我们只更新one，原先的two不需要更改，因为子序列是从前往后的，只有当之后再出现比two小的数的时候再按照第二步那样更改。
     * 假设有如下示例：[2,5,1,6]，在遇到1之后更新了one，后遇到6，因为先判断是否大于two，由于6大于5，就直接返回true了。
     *   1         5       6
     *  one ..... two    three
     *
     * 注意：two附带隐含信息——这之前有个数比two小
     * 所以此时找到的递增子序列不是one、two、three的1 5 6，而是old one、two、three的2 5 6。
     * 这里更新的one的意思是，为之后可能存在的更小的递增子序列打基础。
     * 假设有如下示例：[2,5,1,2,6]，在遇到1之后更新了one，后遇到2，2介于1和5（two）之间，更新two为2，后遇到6，由于6大于2，返回true。
     * 此时找到的递增子序列才是one、two、three的1 2 6
     * 最后考虑one、two的初值，容易想到设定为Integer.MAX_VALUE即可。
     *
     * 作者：xzz666
     * 链接：https://leetcode-cn.com/problems/increasing-triplet-subsequence/solution/pou-xi-ben-zhi-yi-wen-bang-ni-kan-qing-t-3ye2/
     * 来源：力扣（LeetCode）
     * 著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。
     *
     */
    public static boolean increasingTriplet2(int[] nums) {
        int one = Integer.MAX_VALUE;
        int two = Integer.MAX_VALUE;
        for(int num : nums){
            if(num < one){
                one = num;
            }else if(num > one && num < two){
                two = num;
            }else if(num > two){
                return true;
            }
        }
        return false;
    }
}
